package ListBook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TableColumn.SortType;
import javafx.stage.Stage;

import org.controlsfx.dialog.Dialogs;

/**
 * ****************************************
 * FILENAME: ApplicationSettings.java
 * ****************************************
 * Beinhaltet allgemeine Programmeinstellungen
 * u.a. Filter, Window Position, Default Pfade,...
 *
 * @author Kove / HOEFI
 * @since 14.02.2015
 * @version 1.0
 */
@SuppressWarnings("deprecation")
public class ApplicationSettings implements Serializable, ChangeListener<Object> {

  private static final long serialVersionUID = -679220014891970601L;

  /**
   * Singleton-Instanz
   */
  private static ApplicationSettings instance = null;
  /**
   * Programmname u.a. fuer die Anzeige in den Dialogen.
   */
  public static String APP_NAME = "ListBook";
  /**
   * Datei fuer das Speichern der Einstellungen.
   */
  public static String SETTINGS_FILE = System.getProperty("user.dir") + "\\ListBook.settings";
  /**
   * Lock-Datei um eine laufende Instanz erkennen zu koennen.
   */
  public static String LOCK_FILE = System.getProperty("user.dir") + "\\ListBook.lock";
  /**
   * Ordner-Pfad fuer das Speichern der Notiz-Dateien (Windows).
   */
  public static String DATA_PATH = System.getProperty("user.dir") + "\\Notes\\";
  /**
   * Ordner-Pfad fuer das Speichern der Notiz-Dateien (Mac).
   */
  // public static String DATA_PATH = "/Users/srdjankovacevic/Desktop/listBook/";
  /**
   * Default-Pfad fuer den Image-Select-Dialog.
   */
  private String pathImageAddDialog = System.getProperty("user.dir");
  /**
   * Sortierung der TableView fuer die Notizen.
   */
  private SortType sortTypeNotesTable = SortType.ASCENDING;
  /**
   * X-Position des Main-Window in Pixel.
   */
  private double windowPosX = 0;
  /**
   * Y-Position des Main-Window in Pixel.
   */
  private double windowPosY = 0;
  /**
   * Breite des Main-Window in Pixel.
   */
  private double windowWidth = 1280;
  /**
   * Hoehe des Main-Window in Pixel.
   */
  private double windowHeight = 720;
  /**
   * Flag ob das Main-Window maximiert ist.
   */
  private boolean windowMaximized = true;

  /**
   * 'private' Konstruktor um neue Instanzierungen zu verhindern.
   * 
   * @author Kove / HOEFI
   * @since 01.02.2015
   */
  private ApplicationSettings() {
  }

  /**
   * Gibt die Singleton-Instanz zurueck.
   * 
   * @author Kove / HOEFI
   * @since 01.02.2015
   * @return Application-Settings Objekt
   */
  public static ApplicationSettings getInstance() {
    if (instance == null)
      instance = new ApplicationSettings();
    return instance;
  }

  /**
   * @author Kove / HOEFI
   * @since 14.02.2015
   * @return Gibt Klassenmember "pathImageAddDialog" zurueck.
   */
  public String getPathImageAddDialog() {
    return pathImageAddDialog;
  }

  /**
   * @author Kove / HOEFI
   * @since 14.02.2015
   * @param pathImageAddDialog
   *          setzt Klassenmember "pathImageAddDialog".
   */
  public void setPathImageAddDialog(String pathImageAddDialog) {
    this.pathImageAddDialog = pathImageAddDialog;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 14.02.2015
   * @return Gibt Klassenmember "sortTypeNotesTable" zurueck.
   */
  public SortType getSortTypeNotesTable() {
    return sortTypeNotesTable;
  }

  /**
   * @author Kove / HOEFI
   * @since 14.02.2015
   * @param sortTypeNotesTable
   *          setzt Klassenmember "sortTypeNotesTable".
   */
  public void setSortTypeNotesTable(SortType sortTypeNotesTable) {
    this.sortTypeNotesTable = sortTypeNotesTable;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @return Gibt Klassenmember "windowPosX" zurueck.
   */
  public double getWindowPosX() {
    return windowPosX;
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param windowPosX
   *          setzt Klassenmember "windowPosX".
   */
  public void setWindowPosX(double windowPosX) {
    this.windowPosX = windowPosX;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @return Gibt Klassenmember "windowPosY" zurueck.
   */
  public double getWindowPosY() {
    return windowPosY;
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param windowPosY
   *          setzt Klassenmember "windowPosY".
   */
  public void setWindowPosY(double windowPosY) {
    this.windowPosY = windowPosY;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @return Gibt Klassenmember "windowWidth" zurueck.
   */
  public double getWindowWidth() {
    return windowWidth;
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param windowWidth
   *          setzt Klassenmember "windowWidth".
   */
  public void setWindowWidth(double windowWidth) {
    this.windowWidth = windowWidth;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @return Gibt Klassenmember "windowHeight" zurueck.
   */
  public double getWindowHeight() {
    return windowHeight;
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param windowHeight
   *          setzt Klassenmember "windowHeight".
   */
  public void setWindowHeight(double windowHeight) {
    this.windowHeight = windowHeight;
    saveSettings();
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @return Gibt Klassenmember "windowMaximized" zurueck.
   */
  public boolean isWindowMaximized() {
    return windowMaximized;
  }

  /**
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param windowMaximized
   *          setzt Klassenmember "windowMaximized".
   */
  public void setWindowMaximized(boolean windowMaximized) {
    this.windowMaximized = windowMaximized;
    saveSettings();
  }

  /**
   * Ueberwacht Position und Groesse eines Fensters.
   * 
   * @author Kove / HOEFI
   * @since 15.02.2015
   * @param stage
   */
  public void observeWindowPositionAndSize(Stage stage) {
    stage.xProperty().addListener(this);
    stage.yProperty().addListener(this);
    stage.widthProperty().addListener(this);
    stage.heightProperty().addListener(this);
    stage.maximizedProperty().addListener(this);
  }

  /**
   * Speichert die Einstellungen.
   * 
   * @author Kove / HOEFI
   * @since 14.02.2015
   */
  private void saveSettings() {
    try {
      FileOutputStream file = new FileOutputStream(SETTINGS_FILE);
      ObjectOutputStream stream = new ObjectOutputStream(file);
      stream.writeObject(this);
      stream.close();
    } catch (SecurityException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Speichern der Programm-Einstellungen")
      .masthead("Zugriff auf Datei verweigert.")
      .message(e.getMessage())
      .showException(e);
    } catch (FileNotFoundException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Speichern der Programm-Einstellungen")
      .masthead("Datei wurde nicht gefunden.")
      .message(e.getMessage())
      .showException(e);
    } catch (IOException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Speichern der Programm-Einstellungen")
      .masthead("Datei konnte nicht geoeffnet werden.")
      .message(e.getMessage())
      .showException(e);
    }
  }

  /**
   * Laedt die Einstellungen.
   * 
   * @author Kove / HOEFI
   * @since 15.02.2015
   */
  public void loadSettings() {
    try {
      File settingsFile = new File(SETTINGS_FILE);
      // Beim 1. Start der App ist das Settings-File noch nicht vorhanden.
      if (settingsFile.exists()) {
        FileInputStream file = new FileInputStream(settingsFile);
        ObjectInputStream stream = new ObjectInputStream(file);
        instance = (ApplicationSettings) stream.readObject();
        stream.close();
      } else {
        // Wenn Settings-File nicht existiert, dann Default-Settings speichern.
        saveSettings();
      }
    } catch (SecurityException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Laden der Programm-Einstellungen")
      .masthead("Zugriff auf Datei verweigert.")
      .message(e.getMessage())
      .showException(e);
    } catch (ClassNotFoundException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Laden der Programm-Einstellungen")
      .masthead("Fehler bei der Serialisierung der Klasse.")
      .message(e.getMessage())
      .showException(e);
    } catch (IOException e) {
      Dialogs.create()
      .owner(new Stage())
      .title(ApplicationSettings.APP_NAME + ": Laden der Programm-Einstellungen")
      .masthead("Datei konnte nicht geoeffnet werden.")
      .message(e.getMessage())
      .showException(e);
    }
  }

  /**
   * Erstellt alle Ordner inkl. Unterordner bis zum Daten-Pfad.
   * 
   * @author Kove / HOEFI
   * @since 20.02.2015
   */
  public void createDataPath() {
    File dataFolder = new File(DATA_PATH);
    if (!dataFolder.exists())
      dataFolder.mkdirs();
  }

  /*
   * (non-Javadoc)
   * 
   * @see javafx.beans.value.ChangeListener#changed(javafx.beans.value.ObservableValue, java.lang.Object, java.lang.Object)
   */
  @Override
  public void changed(ObservableValue<? extends Object> observableValue, Object oldValue, Object newValue) {
    ReadOnlyProperty<? extends Object> readOnlyProperty = (ReadOnlyProperty<? extends Object>) observableValue;
    // Aufgrund des Namen des ueberwachten Propertys kann der entsprechende Wert gesetzt werden.
    if (readOnlyProperty.getName().equalsIgnoreCase("X"))
      this.setWindowPosX((Double) readOnlyProperty.getValue());
    else if (readOnlyProperty.getName().equalsIgnoreCase("Y"))
      this.setWindowPosY((Double) readOnlyProperty.getValue());
    else if (readOnlyProperty.getName().equalsIgnoreCase("WIDTH"))
      this.setWindowWidth((Double) readOnlyProperty.getValue());
    else if (readOnlyProperty.getName().equalsIgnoreCase("HEIGHT"))
      this.setWindowHeight((Double) readOnlyProperty.getValue());
    else if (readOnlyProperty.getName().equalsIgnoreCase("MAXIMIZED"))
      this.setWindowMaximized((Boolean) readOnlyProperty.getValue());
  }
}
